﻿Namespace Mobs

    Module mMobList

        Public Structure Mob

            Dim MobID As Long
            Dim Shape As Int16
            Dim PositionInfo As EvoLib.Protocol.MobPositionInfo
            Dim TakingHitTempAnim As EvoLib.Protocol.MobAnimation
            Dim MoveTick As Long
            Dim MovementOffsetX As Integer
            Dim MovementOffsetY As Integer

            Dim AnimObj As GUI.MirAnimation
            Dim ShdwObj As GUI.MirAnimation

            Dim mChatText As String
            Dim mChatTimeout As Long

            Dim mNameText As String

            Dim movedistx As Integer
            Dim movedisty As Integer

            Dim VitalPoints As EvoLib.Protocol.Vitals
            Dim Info As EvoLib.Protocol.MobStats

            Dim lstHits As List(Of Player.HitIndicator)
            Dim LastHitUpdateAt As DateTime

            Dim LastUpdateAt As DateTime

        End Structure

        Public lstMobs As New List(Of Mob)

        Public Sub ClearList()

            lstMobs.Clear()

        End Sub

        'Removes any Mob that has strayed outside of the 'Play Area'
        Public Sub FilterMobs()

            'if there are no Mobs in the list, Exit sub
            If lstMobs.Count = 0 Then Exit Sub

            Dim currDT As DateTime = DateTime.Now

            'iterate through every index in lstMobs
            For i As Integer = 0 To lstMobs.Count - 1

                'if we have exceeded the top index, Exit Sub as the index is now Invalid
                If i >= lstMobs.Count Then Exit Sub

                'Check if this Mob falls outside of the 'Play Area'
                If lstMobs(i).PositionInfo.INT2_X < GameWindow.CoordX - Constants.PlayAreaX Or _
                    lstMobs(i).PositionInfo.INT2_Y < GameWindow.CoordY - Constants.PlayAreaY Or _
                    lstMobs(i).PositionInfo.INT2_X > GameWindow.CoordX + Constants.PlayAreaX Or _
                    lstMobs(i).PositionInfo.INT2_Y > GameWindow.CoordY + Constants.PlayAreaY Then

                    'Mob IS outside the play area, so...

                    'Remove this Mob
                    RemoveMob(lstMobs(i).MobID)

                    'call self to remove further Mobs
                    FilterMobs()
                    Exit Sub

                End If

                If lstMobs(i).LastUpdateAt.AddSeconds(60) < currDT Then

                    'TODO: switch to verify mob instead of just removing it as some mobs freeze for longer than 60 seconds

                    RemoveMob(lstMobs(i).MobID)

                    FilterMobs()
                    Exit Sub

                End If

            Next

        End Sub

        Public Sub AddMob(ByVal mobinfo As EvoLib.Protocol.MobAddInfo)

            Dim c As Integer = lstMobs.Count - 1
            If c > -1 Then
                For i As Integer = 0 To c
                    If lstMobs(i).MobID = mobinfo.ServerMobID Then
                        Exit Sub
                    End If
                Next
            End If

            Dim newmob As New Mob

            newmob.MobID = mobinfo.ServerMobID
            newmob.Shape = mobinfo.INT2_SHAPE
            newmob.PositionInfo = mobinfo.Positioninfo
            newmob.VitalPoints = mobinfo.VitalPoints
            newmob.MoveTick = DateTime.Now.Ticks
            newmob.Info = mobinfo.Info

            newmob.AnimObj = New GUI.MirAnimation(GameWindow.GameWin, "Mob" & newmob.MobID, 0, 0)
            AddHandler newmob.AnimObj.AnimStopped, AddressOf StopAnimation
            AddHandler newmob.AnimObj.MouseEvent, AddressOf MobMouseEvent
            newmob.AnimObj.Visible = False

            newmob.ShdwObj = New GUI.MirAnimation(GameWindow.GameWin, "MobShdw" & newmob.MobID, 0, 0, False)
            newmob.ShdwObj.Visible = False

            newmob.mNameText = newmob.Info.Name

            Dim libshape As Libs.LibShapePair = Libs.GetMobLib(newmob.Shape)
            Dim count As Integer = GetAnimCount(libshape, newmob.PositionInfo.BYTE_ANIM)
            Dim tmpanim As GUI.Animation = New GUI.Animation(libshape.library, (libshape.shape * 1000) + 1 + (newmob.PositionInfo.BYTE_ANIM * 80) + (newmob.PositionInfo.BYTE_DIRECTION * 10), count, newmob.PositionInfo.INT8_SPEED)
            If newmob.PositionInfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.Die Then
                'non looping animations - Die (starts on last frame, non looping)
                newmob.AnimObj.StartAnim(tmpanim, False, , count - 1)
            Else
                newmob.AnimObj.StartAnim(tmpanim, True)
            End If

            libshape = Libs.GetMobShadowLib(newmob.Shape)
            Dim tmpanim2 As GUI.Animation = New GUI.Animation(libshape.library, (libshape.shape * 1000) + 1 + (newmob.PositionInfo.BYTE_ANIM * 80) + (newmob.PositionInfo.BYTE_DIRECTION * 10), count, newmob.PositionInfo.INT8_SPEED, Globals.DrawingMode.Shadow)
            newmob.ShdwObj.StartAnim(tmpanim2, True)

            newmob.lstHits = New List(Of Player.HitIndicator)

            newmob.LastUpdateAt = DateTime.Now

            lstMobs.Add(newmob)

        End Sub

        Private Function GetAnimCount(ByVal libshape As Libs.LibShapePair, ByVal anim As EvoLib.Protocol.MobAnimation) As Integer

            Dim c As Integer

            Do
                c += 1

            Loop Until Libs.GetLib(libshape.library).Image((libshape.shape * 1000) + (anim * 80) + c + 1).Texture Is Nothing Or c = 10

            Return c

        End Function

        Public Sub MoveMob(ByVal mobinfo As EvoLib.Protocol.MobUpdateInfo)

            'if no mobs are known, abort
            If lstMobs.Count = 0 Then Exit Sub
            'if the mob referenced in the EvoLib.Protocol.Packet is known to be dead, abort
            If MobIsDead(mobinfo.ServerMobID) And mobinfo.Positioninfo.BYTE_ANIM <> EvoLib.Protocol.MobAnimation.Die Then Exit Sub

            Dim c As Integer = lstMobs.Count - 1
            Dim tmp As Mob = GetMobFromServerID(mobinfo.ServerMobID)

            If tmp.MobID = mobinfo.ServerMobID Then

                lstMobs.Remove(tmp)

                tmp.MoveTick = DateTime.Now.Ticks + tmp.PositionInfo.INT8_SPEED

                If mobinfo.Positioninfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.Walking Then
                    tmp.movedistx = 6
                    tmp.movedisty = 4
                    tmp.PositionInfo = mobinfo.Positioninfo
                    Dim p As Point = Player.CalcNextStep(tmp.PositionInfo.INT2_X, tmp.PositionInfo.INT2_Y, tmp.PositionInfo.BYTE_DIRECTION)
                    tmp.MovementOffsetX = (p.X - tmp.PositionInfo.INT2_X) * Constants.TileWidth
                    tmp.MovementOffsetY = (p.Y - tmp.PositionInfo.INT2_Y) * Constants.TileHeight
                    tmp.PositionInfo.INT2_X = p.X
                    tmp.PositionInfo.INT2_Y = p.Y
                Else
                    tmp.PositionInfo = mobinfo.Positioninfo
                    tmp.movedistx = 0
                    tmp.movedisty = 0
                    tmp.MovementOffsetX = 0
                    tmp.MovementOffsetY = 0
                End If

                Dim libshape As Libs.LibShapePair = Libs.GetMobLib(tmp.Shape)
                Dim animc As Integer = GetAnimCount(libshape, tmp.PositionInfo.BYTE_ANIM)

                Dim tmpanim As GUI.Animation = New GUI.Animation(libshape.library, (libshape.shape * 1000) + 1 + (tmp.PositionInfo.BYTE_ANIM * 80) + (tmp.PositionInfo.BYTE_DIRECTION * 10), animc, tmp.PositionInfo.INT8_SPEED)

                libshape = Libs.GetMobShadowLib(tmp.Shape)

                Dim tmpanim2 As GUI.Animation = New GUI.Animation(libshape.library, (libshape.shape * 1000) + 1 + (tmp.PositionInfo.BYTE_ANIM * 80) + (tmp.PositionInfo.BYTE_DIRECTION * 10), animc, tmp.PositionInfo.INT8_SPEED, Globals.DrawingMode.Shadow)

                If tmp.PositionInfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.Die Or tmp.PositionInfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.TakingHit Then
                    tmp.AnimObj.StartAnim(tmpanim, False)
                    tmp.ShdwObj.StartAnim(tmpanim2, False)
                Else
                    tmp.AnimObj.StartAnim(tmpanim, True)
                    tmp.ShdwObj.StartAnim(tmpanim2, True)
                End If

                tmp.LastUpdateAt = DateTime.Now

                lstMobs.Add(tmp)

                If mobinfo.Positioninfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.MeleeAttack Then
                    Sound.PlayMobSound(tmp.Shape, Sound.MobSounds.Attack)
                ElseIf mobinfo.Positioninfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.TakingHit Then
                    Sound.PlayMobSound(tmp.Shape, Sound.MobSounds.Hit)
                ElseIf mobinfo.Positioninfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.Die Then
                    Sound.PlayMobSound(tmp.Shape, Sound.MobSounds.Die)
                End If

                Exit Sub

            End If

        End Sub

        Public Sub HitMob(ByVal info As EvoLib.Protocol.MobHitInfo)

            'if lstMobs is empty then Exit sub-routine
            If lstMobs.Count = 0 Then Exit Sub

            'set temp Mob object the Mob in the list
            Dim tmp As Mob = GetMobFromServerID(info.ServerMobID)

            'check if the MobHitInfo struct refers to this mob
            If tmp.MobID = info.ServerMobID Then

                'remove the Mob from the list (temporarialy)
                lstMobs.Remove(tmp)

                'calculate the change in HP
                Dim hpdif As Integer
                hpdif = info.VitalPoints.HP - tmp.VitalPoints.HP
                'Add a Hit Indicator for this change
                AddHitIndicator(tmp.lstHits, hpdif)
                'Set the Vital Points struct in 'tmp' to the Latest one in 'info'
                tmp.VitalPoints = info.VitalPoints

                If hpdif < 0 Then tmp.LastHitUpdateAt = DateTime.Now

                'Is this Mob dead (0 HP or less)
                If tmp.VitalPoints.HP <= 0 Then
                    'this Mob is dead, remove the Chat Text by setting the Timeout Ticks to 0
                    tmp.mChatTimeout = 0
                End If

                tmp.LastUpdateAt = DateTime.Now

                're-add the Mob to the list
                lstMobs.Add(tmp)
                'exit the For statement as we have dealt with this mob now and do not need to carry on here

            End If

        End Sub

        Public Sub RemoveMob(ByVal serverid As Int64)

            If lstMobs.Count = 0 Then Exit Sub

            Dim c As Integer = lstMobs.Count - 1

            For i As Integer = 0 To c
                If lstMobs(i).MobID = serverid Then
                    lstMobs(i).ShdwObj.Destroy()
                    lstMobs(i).AnimObj.Destroy()
                    lstMobs.RemoveAt(i)
                    Exit For
                End If
            Next

        End Sub

        Public Sub DrawMobs(ByRef mode As Globals.DrawingMode, ByRef offsetX As Integer, ByRef offsetY As Integer, ByRef originX As Integer, ByRef originY As Integer, ByRef defaultZ As Single, ByRef tc As Long)

            If mode = Globals.DrawingMode.Normal Or mode = Globals.DrawingMode.Line Or mode = Globals.DrawingMode.Shadow Or mode = Globals.DrawingMode.Text Or mode = Globals.DrawingMode.Shadow Or mode = Globals.DrawingMode.Overlay Then


                If lstMobs.Count = 0 Then Exit Sub

                Dim c As Integer = lstMobs.Count - 1
                Dim dx As Integer
                Dim dy As Integer
                Dim z As Single

                'Dim tmpChar As Character

                For i As Integer = 0 To c

                    'tmpChar = lstCharacters(i)

                    Dim M As Mob = lstMobs(i)

                    If mode = Globals.DrawingMode.Overlay Then
                        lstMobs.RemoveAt(i)
                        DoMovement(M, tc, Nothing)
                        lstMobs.Insert(i, M)
                    End If

                    With lstMobs(i) 'tmpChar

                        dx = offsetX + ((.PositionInfo.INT2_X - originX) * Constants.TileWidth) - .MovementOffsetX
                        dy = offsetY + ((.PositionInfo.INT2_Y - originY) * Constants.TileHeight) - .MovementOffsetY
                        z = (defaultZ - ((.PositionInfo.INT2_Y - originY) * 0.005F)) - 0.003F

                        If mode = Globals.DrawingMode.Shadow Then

                            DirectX.GameSprite.Begin(Microsoft.DirectX.Direct3D.SpriteFlags.AlphaBlend)
                            Engine.SetDrawModeShadow(Globals.ShadowBlend)

                            DirectX.GameSprite.Transform = Microsoft.DirectX.Matrix.Transformation(New Microsoft.DirectX.Vector3(dx, dy, 0.0F), Microsoft.DirectX.Quaternion.Identity, New Microsoft.DirectX.Vector3(1.0F, 1.0F, 1.0F), New Microsoft.DirectX.Vector3(dx, dy, z), New Microsoft.DirectX.Quaternion(-0.5F, 0.3F, 0.0F, 0.0F), New Microsoft.DirectX.Vector3(10.0F, 10.0F, 0.0F))

                            .AnimObj.Visible = True

                            .AnimObj.SetPos(dx, dy)
                            .AnimObj.Draw(Globals.DrawingMode.Normal, 0, 0, z + 0.001F) '- 0.02)

                            .AnimObj.Visible = False

                            DirectX.GameSprite.End()

                            DirectX.GameSprite.Transform = Microsoft.DirectX.Matrix.Identity

                        ElseIf mode = Globals.DrawingMode.Normal Then

                            'Draw Hit indicators
                            DrawHitIndicators(lstMobs(i).lstHits, dx, dy)

                            'Draw HP Bar
                            If .LastHitUpdateAt.AddSeconds(10) > DateTime.Now Then
                                If .VitalPoints.MAXHP > .VitalPoints.HP And .VitalPoints.HP > 0 Then
                                    Game.DrawVitalsBar(dx, dy, .VitalPoints, Game.VitalsStyle.MOB_HP_Only)
                                End If
                            End If

                            .AnimObj.Visible = True
                            .AnimObj.SetPos(dx, dy)
                            .AnimObj.Draw(mode, 0, 0, z) '- 0.02)
                            .AnimObj.Visible = False

                            ElseIf mode = Globals.DrawingMode.Text Then

                                With M

                                    If Mouse.MouseOverMobIndex = M.MobID Or (Player.mTargetType = Player.TargetType.Mob And Player.mTargetID = M.MobID And Player.mTargeting) Then
                                        Dim tW As Integer = DirectX.ProFont9.GetNormalTextWidth(M.mNameText)
                                        DirectX.ProFont9.DrawBorderText(M.mNameText, dx + (Constants.TileWidth \ 2) - (tW \ 2), dy - (Constants.TileHeight \ 2), Color.White, Color.Black, Constants.GameTextZ)
                                    End If

                                    If M.mChatTimeout <> 0 Then
                                        If M.mChatTimeout > tc Then
                                            Dim tW As Integer = DirectX.ProFont9.GetNormalTextWidth(M.mChatText)
                                            DirectX.ProFont9.DrawBorderText(M.mChatText, (dx + (Constants.TileWidth \ 2)) - (tW \ 2), dy - CInt(CSng(Constants.TileHeight) * 2.4F), Color.White, Color.Black, Constants.GameTextZ)
                                        Else
                                            lstMobs.RemoveAt(i)
                                            M.mChatTimeout = 0
                                            lstMobs.Add(M)
                                        End If
                                    End If

                                End With


                            ElseIf mode = Globals.DrawingMode.Shadow Then

                                .ShdwObj.Visible = True
                                .ShdwObj.SetPos(dx, dy)
                                .ShdwObj.Draw(mode, 0, 0, z) '- 0.02)
                                .ShdwObj.Visible = False

                            End If

                    End With

                Next

            End If

        End Sub

        Public Sub DoMovement(ByRef tmpmob As Mob, ByRef tc As Long, ByRef donechanges As Boolean)

            With tmpmob

                If .MoveTick <= tc Then

                    .MoveTick = tc + .PositionInfo.INT8_SPEED

                    If tmpmob.movedistx > 0 Or tmpmob.movedisty > 0 Then

                        Select Case .PositionInfo.BYTE_DIRECTION
                            Case EvoLib.Protocol.Direction.N
                                .MovementOffsetY += tmpmob.movedisty
                            Case EvoLib.Protocol.Direction.NE
                                .MovementOffsetY += tmpmob.movedisty
                                .MovementOffsetX -= tmpmob.movedistx
                            Case EvoLib.Protocol.Direction.E
                                .MovementOffsetX -= tmpmob.movedistx
                            Case EvoLib.Protocol.Direction.SE
                                .MovementOffsetY -= tmpmob.movedisty
                                .MovementOffsetX -= tmpmob.movedistx
                            Case EvoLib.Protocol.Direction.S
                                .MovementOffsetY -= tmpmob.movedisty
                            Case EvoLib.Protocol.Direction.SW
                                .MovementOffsetY -= tmpmob.movedisty
                                .MovementOffsetX += tmpmob.movedistx
                            Case EvoLib.Protocol.Direction.W
                                .MovementOffsetX += tmpmob.movedistx
                            Case EvoLib.Protocol.Direction.NW
                                .MovementOffsetY += tmpmob.movedisty
                                .MovementOffsetX += tmpmob.movedistx
                        End Select

                        If .MovementOffsetX = 0 Then
                            .movedistx = 0
                        End If

                        If .MovementOffsetY = 0 Then
                            .movedisty = 0
                        End If

                        'If Not .ChatTimeout = 0 Then
                    'If .ChatTimeout <= tc Then
                    'tmpmob.ChatTimeout = 0
                    'tmpmob.ChatMessage.Visible = False
                    'End If
                    'End If

                End If

                donechanges = True
                        Else
                donechanges = False

                        End If

            End With

        End Sub

        Public Sub StopAnimation(ByVal name As String)

            If lstMobs.Count = 0 Then Exit Sub

            Dim mobID As Integer = CInt(Right(name, name.Length - 3))
            Dim c As Integer = lstMobs.Count - 1
            Dim tmp As Mob

            For i As Integer = 0 To c
                If lstMobs(i).MobID = mobID Then

                    tmp = lstMobs(i)

                    If tmp.PositionInfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.TakingHit Then

                        Dim mobinfo As EvoLib.Protocol.MobUpdateInfo
                        mobinfo.ServerMobID = mobID
                        mobinfo.Positioninfo = tmp.PositionInfo
                        mobinfo.Positioninfo.BYTE_ANIM = EvoLib.Protocol.MobAnimation.Standing

                        MoveMob(mobinfo)

                    End If

                    Exit Sub

                End If
            Next
        End Sub

        Public Sub MobMouseEvent(ByRef type As Mouse.MouseInputType, ByRef e As MouseEventArgs, ByVal name As String)

            If lstMobs.Count > 0 Then

                Dim mobID As Integer = CInt(Right(name, name.Length - 3))
                Dim c As Integer = lstMobs.Count - 1

                For i As Integer = 0 To c
                    If lstMobs(i).MobID = mobID Then

                        GameWindow.MouseOverMob = True
                        Mouse.MouseOverMobIndex = lstMobs(i).MobID

                        InterfaceBar.AvatarImg = lstMobs(i).Info.Avatar
                        InterfaceBar.AvatarLib = EvoLib.Protocol.ImgLibs.MonImg

                        If type = Mouse.MouseInputType.MouseClick Then

                            If lstMobs(i).VitalPoints.HP > 0 Then

                                Dim tmpx As Integer, tmpy As Integer
                                tmpx = lstMobs(i).PositionInfo.INT2_X - GameWindow.CoordX
                                tmpy = lstMobs(i).PositionInfo.INT2_Y - GameWindow.CoordY
                                If tmpx > -2 And tmpx < 2 And tmpy > -2 And tmpy < 2 Then

                                    Select Case tmpx
                                        Case -1
                                            Select Case tmpy
                                                Case -1
                                                    Player.ChangePointerDirection(EvoLib.Protocol.Direction.NW)
                                                Case 0
                                                    Player.ChangePointerDirection(EvoLib.Protocol.Direction.W)
                                                Case 1
                                                    Player.ChangePointerDirection(EvoLib.Protocol.Direction.SW)
                                            End Select
                                        Case 1
                                            Select Case tmpy
                                                Case -1
                                                    Player.ChangePointerDirection(EvoLib.Protocol.Direction.NE)
                                                Case 0
                                                    Player.ChangePointerDirection(EvoLib.Protocol.Direction.E)
                                                Case 1
                                                    Player.ChangePointerDirection(EvoLib.Protocol.Direction.SE)
                                            End Select
                                        Case 0
                                            Select Case tmpy
                                                Case -1
                                                    Player.ChangePointerDirection(EvoLib.Protocol.Direction.N)
                                                Case 1
                                                    Player.ChangePointerDirection(EvoLib.Protocol.Direction.S)
                                            End Select
                                    End Select

                                End If
                                Player.Attack(Player.TargetType.Mob, mobID)
                                Exit Sub
                            End If
                        End If

                        'GameWindow.GameWin_MouseEvent(type, e, True)

                        Exit Sub

                    End If
                Next
            End If

        End Sub

        Public Function GetMobPos(ByVal id As Integer) As Point

            For Each m As Mob In lstMobs

                If m.MobID = id Then

                    Return New Point(m.PositionInfo.INT2_X, m.PositionInfo.INT2_Y)

                End If

            Next

            Return New Point(0, 0)

        End Function

        Public Function MobIsDead(ByVal id As Integer) As Boolean

            For Each m As Mob In lstMobs

                If m.MobID = id Then

                    If m.VitalPoints.HP <= 0 Then
                        Return True
                    Else
                        Return False
                    End If

                End If

            Next

            Return False

        End Function

        Public Function GetMobFromServerID(ByVal serverID As Int64) As Mob

            For Each m As Mob In lstMobs

                If m.MobID = serverID Then

                    Return m

                End If

            Next

            Dim pkt As EvoLib.Protocol.Packet
            pkt.PacketType = EvoLib.Protocol.GamePacketType.MobAddUpdate
            pkt.PacketOpCode = EvoLib.Protocol.PacketOpCode.Enquiry
            pkt.Username = Globals.sUsername

            ClientSock.SendData(EvoLib.Protocol.MakePacket(pkt, serverID, Globals.Crypto))

            Return Nothing

        End Function

        Public Function GetMobIDFromPos(ByVal x As Integer, ByVal y As Integer) As Int64

            For Each m As Mob In lstMobs

                If m.PositionInfo.INT2_X = x And m.PositionInfo.INT2_Y = y Then

                    Return m.MobID

                End If

            Next

            Return Nothing

        End Function

        Public Sub Reset()

            lstMobs.Clear()

        End Sub

        Public Sub ChatMessage(ByRef ChatMsg As EvoLib.Protocol.MobChatMsg, ByRef tc As Long)


            For I As Integer = 0 To lstMobs.Count - 1

                'check if this Mob is the one that this EvoLib.Protocol.Packet concerns
                If lstMobs(I).MobID = ChatMsg.ServerMobID Then

                    'temp Mob object to store the Mob
                    Dim M As Mob = lstMobs(I)

                    Select Case ChatMsg.BYTE_TYPE

                        Case EvoLib.Protocol.ChatMsgType.Normal

                            'remove original copy of Mob from list
                            lstMobs.RemoveAt(I)
                            'setup Chat Message text and Timeout Ticker
                            M.mChatText = M.mNameText & ": " & ChatMsg.M.STR_MSG
                            M.mChatTimeout = tc + Constants.ChatMessageDisplayTime
                            're-add updated Mob
                            lstMobs.Add(M)

                    End Select

                    'exit sub-routine as we have now found the mob concerned by this EvoLib.Protocol.Packet and updated it, therefore, nothing left to do here
                    Exit Sub

                End If

            Next


        End Sub

    End Module

End Namespace
